import os
import argparse
from datetime import datetime
import fnmatch


def is_text_file(filename):
  # 判断文件是否为文本文件的一种简单方法
  text_extensions = ['.txt', '.md', '.csv', '.py', '.js', '.html', '.json', '.bat', '.css']
  return any(filename.lower().endswith(ext) for ext in text_extensions)


def format_size(size_in_bytes):
  # 格式化文件大小
  for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
    if size_in_bytes < 1024.0:
      return f"{size_in_bytes:.2f} {unit}"
    size_in_bytes /= 1024.0
  return "Unknown"


def read_gitignore(directory):
  gitignore_path = os.path.join(directory, '.gitignore')
  if not os.path.exists(gitignore_path):
    return []

  ignore_patterns = []
  with open(gitignore_path, 'r', encoding='utf-8') as file:
    for line in file:
      line = line.strip()
      if line and not line.startswith('#'):
        ignore_patterns.append(line)
  return ignore_patterns


def should_ignore(path, ignore_patterns):
  ignore_patterns += ['.git']
  for pattern in ignore_patterns:
    if fnmatch.fnmatch(path, pattern):
      return True
    if any(fnmatch.fnmatch(p, pattern) for p in path.split(os.sep)):
      return True
    if pattern.endswith('/') and path.startswith(pattern.rstrip('/')):
      return True
    if pattern.startswith('**/'):
      pattern = pattern[3:]
      if fnmatch.fnmatch(path, pattern):
        return True
  return False


def get_code_type(filename):
  if filename.lower().endswith('.py'):
    return 'python'
  if filename.lower().endswith('.json'):
    return 'JSON'
  if filename.lower().endswith('.js'):
    return 'javascript'
  return ''


def file_tree_to_markdown(directory, output_file='file_tree.md', white_files=None):
  ignore_patterns = read_gitignore(directory)
  ignore_patterns = [p[1:] for p in ignore_patterns if p.startswith('/')] + ignore_patterns

  with open(output_file, 'w', encoding='utf-8') as md_file:
    for root, dirs, files in os.walk(directory):
      # 过滤掉应该忽略的目录
      dirs[:] = [d for d in dirs if not should_ignore(os.path.join(root, d), ignore_patterns)]
      level = root.replace(directory, '').count(os.sep)
      indent = ' ' * 4 * (level)
      md_file.write(f"{indent}- {os.path.basename(root)}\n")

      sub_indent = ' ' * 4 * (level + 1)
      for filename in files:
        filepath = os.path.join(root, filename)

        # 过滤掉应该忽略的文件
        if should_ignore(filepath, ignore_patterns):
          continue
        if white_files is not None and filename not in white_files:
          continue
        file_stat = os.stat(filepath)
        file_size = format_size(file_stat.st_size)
        mod_time = datetime.fromtimestamp(file_stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S')

        md_file.write(f"{sub_indent}- **{filename}**\n")
        md_file.write(f"{sub_indent}  - Size: {file_size}\n")
        md_file.write(f"{sub_indent}  - Modified: {mod_time}\n")

        if is_text_file(filename):
          try:
            with open(filepath, 'r', encoding='utf-8') as file:
              content = file.read()
            if len(content) > 0:
              md_file.write(f"{sub_indent}  - Content: \n```{get_code_type(filename)}\n{content}\n```\n")
            else:
              md_file.write(f"{sub_indent}  - Content: \n 无内容 \n")
          except Exception as e:
            md_file.write(f"{sub_indent}  - Error reading file: {e} \n")
        md_file.write(f"---\n")


def main():
    parser = argparse.ArgumentParser(description="Generate a file tree in Markdown format.")
    parser.add_argument("directory", type=str, help="The directory to generate the file tree for.")
    parser.add_argument("--output-file", type=str, default="file_tree.md", help="The output Markdown file (default: file_tree.md).")
    parser.add_argument("--white-files", nargs="+", default=None, help="List of files to include in detail (whitelist).")

    args = parser.parse_args()

    file_tree_to_markdown(args.directory, args.output_file, args.white_files)


if __name__ == "__main__":
    main()
